/*  Copyright (C) 2008  Alexander Blyzniuchenko (BlezAlex)

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

using System;
using System.Collections;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;


namespace MediaDC.Native
{
    [StructLayout(LayoutKind.Sequential)]
    struct SHFILEINFO
    {
        public IntPtr hIcon;
        public int iIcon;
        public uint dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }

    static class NativeImageList
    {
        #region Win32Constants

        private const uint SHGFI_LARGEICON = 0x0; // 'Large icon
        private const uint SHGFI_SMALLICON = 0x1; // 'Small icon
        private const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
        private const uint SHGFI_SHELLICONSIZE = 0x4;
        private const uint SHGFI_SYSICONINDEX = 0x4000;
        private const uint SHGFI_ICON = 0x100;

        private const uint FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
        private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;

        private const uint LVM_FIRST = 0x1000;
        private const uint LVM_SETIMAGELIST = LVM_FIRST + 3;
        private const uint LVSIL_NORMAL = 0;
        private const uint LVSIL_SMALL = 1;

        private const uint TVM_SETIMAGELIST = 0x1109;
        private const uint TVSIL_NORMAL = 0;
        private const uint TVSIL_STATE = 2;

        #endregion


        private static readonly int _directoryIconIndex;
        private static readonly IntPtr _largeImageList;
        private static readonly IntPtr _smallImageList;
        private static readonly Hashtable _imageIndexCache = new Hashtable();
        private static readonly Hashtable _imageCache = new Hashtable();

        static NativeImageList()
        {
            _directoryIconIndex = FileIconIndex("a", true);
            SHFILEINFO fileInfo = new SHFILEINFO();

            _largeImageList = SHGetFileInfo("", 0, ref fileInfo, Marshal.SizeOf(fileInfo),
                SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX | SHGFI_LARGEICON);

            _smallImageList = SHGetFileInfo("", 0, ref fileInfo, Marshal.SizeOf(fileInfo),
                SHGFI_SHELLICONSIZE | SHGFI_SYSICONINDEX | SHGFI_SMALLICON);
        }


        #region DLLImport
        [DllImport("shell32.dll")]
        private static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);

        [DllImport("Shell32.dll")]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, int cbfileInfo, uint uFlags);

        [DllImport("User32.DLL")]
        public static extern int SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

        #endregion DLLImport

        public static void SetListViewIconIndex(IntPtr controlHandle)
        {
            SendMessage(controlHandle, LVM_SETIMAGELIST, (IntPtr)LVSIL_NORMAL, _largeImageList);
            SendMessage(controlHandle, LVM_SETIMAGELIST, (IntPtr)LVSIL_SMALL, _smallImageList);
        }

        public static void SetTreeViewIconIndex(IntPtr controlHandle)
        {
            SendMessage(controlHandle, TVM_SETIMAGELIST, (IntPtr)TVSIL_NORMAL, _smallImageList);
            SendMessage(controlHandle, TVM_SETIMAGELIST, (IntPtr)TVSIL_STATE, _smallImageList);
        }

        public static int FileIconIndex(string fileName)
        {
            return FileIconIndex(fileName, false);
        }

        public static int DirectoryIconIndex
        {
            get { return _directoryIconIndex; }
        }

        public static Image GetFileIcon(string ext)
        {
            lock (_imageCache)
            {
                if (_imageCache.ContainsKey(ext))
                {
                    return (Image)_imageCache[ext];
                }
                else 
                {
                    SHFILEINFO shinfo = new SHFILEINFO();
                    SHGetFileInfo(ext, FILE_ATTRIBUTE_NORMAL, ref shinfo, (uint)Marshal.SizeOf(shinfo),
                                  SHGFI_ICON | SHGFI_SMALLICON | SHGFI_USEFILEATTRIBUTES);
                    
                    Image i = Icon.FromHandle(shinfo.hIcon).ToBitmap();
                    _imageCache.Add(ext, i);
                    return i;
                }
            }
        }

        private static int FileIconIndex(string fileName, bool isDirectory)
        {
            string ext = Path.GetExtension(fileName);
            lock (_imageIndexCache)
            {
                if (_imageIndexCache.ContainsKey(ext))
                {
                    return (int)_imageIndexCache[ext];
                }
                else
                {
                    SHFILEINFO fileInfo = new SHFILEINFO();
                    SHGetFileInfo(fileName, isDirectory ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_NORMAL,
                        ref fileInfo,
                        Marshal.SizeOf(fileInfo), SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);

                    _imageIndexCache.Add(ext, fileInfo.iIcon); 
                    return fileInfo.iIcon;
                }
            }
        }
    }
}
